﻿using System;
using System.Collections.Generic;
using System.Linq;
using NetTopologySuite.Geometries;
using NetTopologySuite.Operation.Polygonize;

namespace PointAsLine
{
    public class PolygonBuilder
    {
        /// <summary>
        /// 更精确的构建方法（处理复杂情况）
        /// </summary>
        public static List<Polygon> BuildPolygonsAdvanced(IEnumerable<Tuple<PointF, PointF>> lines)
        {
            var geometryFactory = new GeometryFactory();

            // 1. 创建线串集合
            var lineStrings = new List<LineString>();
            foreach (var line in lines)
            {
                var coord1 = new Coordinate(line.Item1.X, line.Item1.Y);
                var coord2 = new Coordinate(line.Item2.X, line.Item2.Y);
                lineStrings.Add(geometryFactory.CreateLineString(new[] { coord1, coord2 }));
            }

            // 2. 合并所有线串为一个几何集合
            var linesGeometry = geometryFactory.BuildGeometry(lineStrings);

            // 3. 执行线串合并（连接断开的线段）
            var mergedLines = linesGeometry.Union();

            // 4. 多边形化
            var polygonizer = new Polygonizer();
            polygonizer.Add(mergedLines);

            // 5. 获取并验证多边形
            var polygons = polygonizer.GetPolygons()
                .Cast<Polygon>()
                .Where(p => p.IsValid && !p.IsEmpty && p.Area > 0)
                .ToList();

            return polygons;
        }

        public static void ExtentsPolyline(List<Polyline> lines)
        {
            for (var i = 0; i < lines.Count; i++)
            {
                var line1 = lines[i];

                for (var j = i + 1; j < lines.Count; j++)
                {
                    var line2 = lines[j];

                    PolylineProjector.ProjectLine(line1, line2, 5);
                }
            }
        }

        internal static void ExtentsPolyline(List<List<PointF2G>> simplifiedSegments)
        {
            List<Polyline> lines=new List<Polyline>();
            for (var i = 0; i < simplifiedSegments.Count; i++)
            {
                var line = new Polyline() { Points = simplifiedSegments[i] };
                lines.Add(line);
            }
            ExtentsPolyline(lines);
        }
    }
    public static class PolylineProjector
    {
        /// <summary>
        /// 将两条折线的端点互相投影到对方线段上（如果距离在允许范围内）
        /// </summary>
        /// <param name="polyline1">第一条折线</param>
        /// <param name="polyline2">第二条折线</param>
        /// <param name="allowDistance">允许投影的最大距离</param>
        public static void ProjectLine(Polyline polyline1, Polyline polyline2, double allowDistance)
        {
            // 处理polyline1的端点
            ProjectEndPoints(polyline1, polyline2, allowDistance);
            // 处理polyline2的端点
            ProjectEndPoints(polyline2, polyline1, allowDistance);
        }

        private static void ProjectEndPoints(Polyline sourceLine, Polyline targetLine, double allowDistance)
        {
            if (sourceLine.Points.Count == 0 || targetLine.Points.Count < 2)
                return;

            // 获取起点和终点
            PointF2G startPoint = sourceLine.Points[0];
            PointF2G endPoint = sourceLine.Points[sourceLine.Points.Count - 1];

            // 处理起点
            PointF2G? projectedStart = FindProjectionPoint(startPoint, targetLine, allowDistance);
            if (projectedStart!=null)
            {
                sourceLine.Points[0] = projectedStart;
            }

            // 处理终点
            PointF2G? projectedEnd = FindProjectionPoint(endPoint, targetLine, allowDistance);
            if (projectedEnd!=null)
            {
                sourceLine.Points[sourceLine.Points.Count - 1] = projectedEnd;
            }
        }

        private static PointF2G? FindProjectionPoint(PointF2G point, Polyline line, double maxDistance)
        {
            var pt = new PointF2G(point.X, point.Y);
            var nearestPoint = NearestPoints(line, pt);
            if (nearestPoint==null) { return null; }

            if (PointFFunc.Distance(nearestPoint,pt) <= maxDistance)
            {
                return new PointF2G((float)nearestPoint.X, (float)nearestPoint.Y);
            }
            return null;
        }

        private static PointF2G? NearestPoints(Polyline line, PointF2G point)
        {
            double minDistance = double.MaxValue;
            PointF2G nearestPoint = null;

            // 检查每条线段
            for (int i = 0; i < line.Points.Count - 1; i++)
            {
                var segStart = line.Points[i];
                var segEnd = line.Points[i + 1];

                // 计算点到线段的最近点
                var closest = ClosestPointOnSegment(point, segStart, segEnd);
                double dist = PointFFunc.Distance(point,closest);

                if (dist < minDistance)
                {
                    minDistance = dist;
                    nearestPoint = closest;
                }
            }
            if (nearestPoint != null)
            {
                return new PointF2G((float)point.X, (float)point.Y);
            }
            return null;
        }

        private static PointF2G ClosestPointOnSegment(PointF2G pt, PointF2G segStart, PointF2G segEnd)
        {
            double dx = segEnd.X - segStart.X;
            double dy = segEnd.Y - segStart.Y;

            // 线段长度为0的情况
            if (dx == 0 && dy == 0)
                return segStart;

            // 计算投影比例
            double t = ((pt.X - segStart.X) * dx + (pt.Y - segStart.Y) * dy) / (dx * dx + dy * dy);

            // 检查投影点是否在线段上
            if (t < 0) return segStart;
            if (t > 1) return segEnd;

            // 计算投影点坐标
            return new PointF2G(
                (float)(segStart.X + t * dx),
                (float)(segStart.Y + t * dy));
        }
    }
}
